/*
 * Matrix4x4.h
 *
 *  Created on: 02-11-2012
 *      Author: Olej-i5
 */

#ifndef MATRIX4X4_H_
#define MATRIX4X4_H_

#include <GlobalData.h>
#include <Math/EMatrix3x3.h>
#include "EVector3.h"
#include "EQuaternion.h"

/**
 * Matrix4x4 assumes column-major order as default, as this format is expected by OpenGL.
 * That way the first 4 members in memory (of this->data) describe side vector and further ones describe up, forward and translation.
 */
template <typename TYPE>
class Matrix4x4
{
private:
	TYPE data2D[4][4];

public:

#pragma region Accessors
	// Ideally, switching column and row indexing order in all accessors should result in acquiring row-major order as the implementation is being done with that in mind, but it should be double checked before enabling such functionality.
	// NOTE: this will not work as matrices are passed to shaders by an address of at(0, 0).
	inline TYPE& at(uint row, uint column);
	inline TYPE get(uint row, uint column) const;
	inline void set(uint row, uint column, TYPE value);
#pragma endregion

    Matrix4x4();
    Matrix4x4(const Matrix4x4& other);
    Matrix4x4(const Vector3<TYPE>& translation);

    static Matrix4x4<TYPE>	getScalingMatrix		(Vector3<TYPE> scale);
    static Matrix4x4<TYPE>	getRotationMatrix		(Quaternion<TYPE> q);
    static Matrix4x4<TYPE>	getTranslationMatrix	(Vector3<TYPE> translation);


    Matrix4x4& 				setIdentity				();
    static Matrix4x4<TYPE> 	getIdentity				();
    Matrix3x3<TYPE> 		get3x3					() const;
    Matrix4x4<TYPE>& 		transpose				();
    Matrix4x4<TYPE>  		getTransposed			() const;
    Matrix4x4<TYPE>  		invert					();
    Matrix4x4<TYPE> 		getInverted				() const;

    Vector3<TYPE> 			side					() const;
    Vector3<TYPE> 			up						() const;
    Vector3<TYPE> 			forward					() const;
    Vector3<TYPE> 			translation				() const;
    Matrix4x4<TYPE>& 		translate				(Vector3<TYPE> translation);
    void 					operator*=				(const Matrix4x4<TYPE>& other);

    // TODO: Check order.
//    static Matrix4x4 createRotationX(TYPE angle);
//    static Matrix4x4 createRotationY(TYPE angle);
//    static Matrix4x4 createRotationZ(TYPE angle);
//    Matrix4x4& rotateX(TYPE angle);
//    Matrix4x4& rotateY(TYPE angle);
//    Matrix4x4& rotateZ(TYPE angle);
};

template <typename TYPE>
Matrix4x4<TYPE> operator*(const Matrix4x4<TYPE>& a, const Matrix4x4<TYPE>& b);

#endif /* MATRIX4X4_H_ */
